package com.sny.service.impl;


import com.sny.constants.FileConstants;
import com.sny.constants.ResultForAmis;
import com.sny.dto.FileStorageDto;
import com.sny.dto.FileUploadDTO;
import com.sny.service.IAmisChunkFileService;
import com.sny.service.IFileStorageService;
import com.sny.util.CodecUtil;
import com.sny.util.DateUtil;
import com.sny.util.FileUtil;
import com.sny.vo.param.AmisFinishChunkParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class AmisChunkFileServiceImpl implements IAmisChunkFileService {

    @Value("${upload.root.dir}")
    private String uploadRootDir;

    @Value("${upload.amisChunkSize}")
    private long amisChunkSize;

    @Autowired
    private IFileStorageService fileInfoService;

    public ResultForAmis startChunkApi(String filename, String name, String appId) {
        log.info("filename: {};name:{}", filename, name);
        try {
            Map<String, Object> resultMap = new HashMap<>();
            String uploadId = CodecUtil.UUID();
            // 创建临时文件
            String fileSuffixUrl = FileConstants.FILE_SEPARATORCHAR + appId + FileConstants.FILE_SEPARATORCHAR + "tmp" + FileConstants.FILE_SEPARATORCHAR +uploadId;
            String filedirPath = uploadRootDir + fileSuffixUrl;
            File file = new File( filedirPath + FileConstants.FILE_SEPARATORCHAR +uploadId);
            //判断文件夹是否存在，如果不存在就创建
            FileUtil.isExist(filedirPath);
            boolean fileFlag = file.createNewFile();
            resultMap.put("key", fileSuffixUrl);
            resultMap.put("uploadId", uploadId);
            return ResultForAmis.success(resultMap);
        } catch (Exception e) {
            return ResultForAmis.fail("上传失败");
        }
    }

    @Override
    public ResultForAmis chunkApi(String key, String uploadId, Long partNumber, Long partSize, MultipartFile file, String appId) {
        log.info("key: {};uploadId:{};partNumber:{};appId{}", key, uploadId, partNumber, appId);
        if (null != file) {
            try {
                // 将原来的文件与新文件进行合并
                String uploadDirPath = uploadRootDir + FileConstants.FILE_SEPARATORCHAR + appId + FileConstants.FILE_SEPARATORCHAR + "tmp" + FileConstants.FILE_SEPARATORCHAR +uploadId + FileConstants.FILE_SEPARATORCHAR +uploadId;
                File tmpFile = new File(uploadDirPath);
                RandomAccessFile accessTmpFile = new RandomAccessFile(tmpFile, "rw");
                //这个必须与前端设定的值一致
                long offset = amisChunkSize * 1024 * 1024 * ( partNumber - 1 );
                //定位到该分片的偏移量
                accessTmpFile.seek(offset);
                //写入该分片数据
                accessTmpFile.write(file.getBytes());
                accessTmpFile.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
            String fileUuid = CodecUtil.UUID();
            Map<String, String> retMap = new HashMap<>();
            retMap.put("eTag", fileUuid);
            return ResultForAmis.success(retMap);
        } else {
            return ResultForAmis.fail("上传文件不能为空");
        }
    }

    @Override
    public ResultForAmis finishChunkApi(AmisFinishChunkParam param, String appId) {
        log.info("appId: {};param: {}", appId, param);
        Map<String, Object> resultMap = new HashMap<>();
        try {
            String filePath = uploadRootDir + FileConstants.FILE_SEPARATORCHAR + appId + FileConstants.FILE_SEPARATORCHAR + "tmp" +
                    FileConstants.FILE_SEPARATORCHAR +param.getUploadId() + FileConstants.FILE_SEPARATORCHAR +param.getUploadId();
            File mergerFile = new File(filePath);
            //目录创建完毕，移动文件
            String fileExt = FileUtil.getExtension(param.getFilename());
            FileUploadDTO fileUploadDTO = moveFile(mergerFile, param.getUploadId(), fileExt,param.getFilename(),appId);
            long size = 0;
            String fileUrl = "";
            if (fileUploadDTO.isUploadComplete()) {
                log.info("upload complete !!" + fileUploadDTO.isUploadComplete() + " name=" + param.getFilename());
                //保存文件信息到数据库
                FileStorageDto fileInfo = new FileStorageDto();
                fileInfo.setName(param.getUploadId());
                fileInfo.setSize(fileUploadDTO.getSize());
                fileInfo.setSuffix(fileUploadDTO.getFileExt());
                fileInfo.setAppId(appId);
                fileInfo.setFilename(param.getFilename());
                fileInfo.setFileId(param.getUploadId());
                String storageDir = FileConstants.FILE_SEPARATORCHAR + appId + FileConstants.FILE_SEPARATORCHAR + FileUtil.getFileType(fileInfo.getFilename());
                //  /+appid+/+doc|zip
                fileInfo.setStorageDir(storageDir);
                //  /+appid+/+doc|zip/
                fileInfo.setUrl(storageDir + FileConstants.FILE_SEPARATORCHAR);
                fileInfoService.save(fileInfo);
                size = fileUploadDTO.getSize();
                fileUrl = "/fms/api/show/" + fileInfo.getName() + "_0/" + fileInfo.getFilename();
            }
            resultMap.put("filename", param.getFilename());
            resultMap.put("mimetype", fileExt);
            resultMap.put("size", size);
            resultMap.put("value", fileUrl);
            return ResultForAmis.success(resultMap);
        } catch (Exception e) {
            return ResultForAmis.fail("上传文件失败，重新上传");
        }
    }


    /**
     * 移动文件
     *
     * @param sourceFile 将要移动的文件
     * @param fileId     文件id
     */
    public FileUploadDTO moveFile(File sourceFile, String fileId, String fileExt,String fileName,String appId) {
        //检查要重命名的文件是否存在，是否是文件
        FileUploadDTO fileUploadDTO = new FileUploadDTO();
        if (!sourceFile.exists() || sourceFile.isDirectory()) {
            log.info("File does not exist: {}", fileName);
            fileUploadDTO.setUploadComplete(false);
            return fileUploadDTO;
        }
        String oldPath = sourceFile.getPath();
        log.info("oldPath:{}", oldPath);
        String filePath = uploadRootDir + FileConstants.FILE_SEPARATORCHAR + appId + FileConstants.FILE_SEPARATORCHAR +
                FileUtil.getFileType(fileName);
        //判断文件夹是否存在，如果不存在就创建
        FileUtil.isExist(filePath);
        String path = filePath + FileConstants.FILE_SEPARATORCHAR + fileId + "." + fileExt;
        File newFile = new File(path);
        //移动文件
        boolean uploadFlag = sourceFile.renameTo(newFile);
        log.info("uploadFlag:{}", uploadFlag);
        // 删除临时文件目录
        delteOldDir(sourceFile, fileId);
        fileUploadDTO.setMtime(DateUtil.getCurrentTimeStamp());
        fileUploadDTO.setUploadComplete(uploadFlag);
        fileUploadDTO.setPath(path);
        fileUploadDTO.setSize(newFile.length());
        fileUploadDTO.setFileExt(fileExt);
        fileUploadDTO.setFileId(fileId);
        return fileUploadDTO;
    }

    // 删除临时文件目录
    private void delteOldDir(File sourceFile, String fileId) {
        Path oldDirPath = Paths.get(sourceFile.getParent());
        try {
            Files.walkFileTree(oldDirPath,
                new SimpleFileVisitor<Path>() {
                    // 先去遍历删除文件
                    @Override
                    public FileVisitResult visitFile(Path file,
                                                     BasicFileAttributes attrs) throws IOException {
                        Files.delete(file);
                        return FileVisitResult.CONTINUE;
                    }

                    // 再去遍历删除目录
                    @Override
                    public FileVisitResult postVisitDirectory(Path dir,
                                                              IOException exc) throws IOException {
                        Files.delete(dir);
                        return FileVisitResult.CONTINUE;
                    }
                }
            );
        } catch (IOException e) {
            log.info("删除文件失败 oldDirPath: {}", oldDirPath);
        }
    }
}
